#include "precompiled.h"
#pragma hdrstop

#if (INF_COMPILER==COMPILER_MSVC && INF_COMP_VER>=1300)
#include <intrin.h>
#endif

namespace inf {

	struct cpuFeatures_t {
		char CPUString[0x20];
		char CPUBrandString[0x40];

		int nSteppingID;
		int nModel;
		int nFamily;
		int nProcessorType;
		int nExtendedmodel;
		int nExtendedfamily;
		int nBrandIndex;
		int nCLFLUSHcachelinesize;
		int nAPICPhysicalID;
		int nFeatureInfo;
		int nCacheLineSize;
		int nL2Associativity;
		int nCacheSizeK;
		int nPhysicalAddress;
		int nVirtualAddress;

		int nlogicalProcessors;
		int nCores;
		bool bMultithreading;	// HTT
	};

	cpuFeatures_t cpuFeature;
	unsigned int cpuid_flags;

	void	SysCpu::init() {
		int CPUInfo[4] = {-1};
		int nRet = 0;
		unsigned    nIds, nExIds, i;

		memset( &cpuFeature, 0, sizeof(cpuFeature) );

		// __cpuid with an InfoType argument of 0 returns the number of
		// valid Ids in CPUInfo[0] and the CPU identification string in
		// the other three array elements. The CPU identification string is
		// not in linear order. The code below arranges the information 
		// in a human readable form.
		__cpuid(CPUInfo, 0);
		nIds = CPUInfo[0];
		memset(cpuFeature.CPUString, 0, sizeof(cpuFeature.CPUString));
		*((int*)cpuFeature.CPUString) = CPUInfo[1];
		*((int*)(cpuFeature.CPUString+4)) = CPUInfo[3];
		*((int*)(cpuFeature.CPUString+8)) = CPUInfo[2];

		if( strstr(cpuFeature.CPUString, "Intel") )
			cpuid_flags = cpuid_flags | CPUID_INTEL;
		else if( strstr(cpuFeature.CPUString, "AMD") )
			cpuid_flags = cpuid_flags | CPUID_AMD;
		else {
			cpuid_flags = CPUID_UNKNOWN;
			return;
		}

		// get the information associated with each valid Id
		for (i=0; i<=nIds; ++i)
		{
			__cpuid(CPUInfo, i);

			// Interpret CPU feature information.
			if  (i == 1)
			{
				cpuFeature.nSteppingID = CPUInfo[0] & 0xf;
				cpuFeature.nModel = (CPUInfo[0] >> 4) & 0xf;
				cpuFeature.nFamily = (CPUInfo[0] >> 8) & 0xf;
				cpuFeature.nProcessorType = (CPUInfo[0] >> 12) & 0x3;
				cpuFeature.nExtendedmodel = (CPUInfo[0] >> 16) & 0xf;
				cpuFeature.nExtendedfamily = (CPUInfo[0] >> 20) & 0xff;
				cpuFeature.nBrandIndex = CPUInfo[1] & 0xff;
				cpuFeature.nCLFLUSHcachelinesize = ((CPUInfo[1] >> 8) & 0xff) * 8;
				cpuFeature.nlogicalProcessors = ((CPUInfo[1] >> 16) & 0xff);
				cpuFeature.nAPICPhysicalID = (CPUInfo[1] >> 24) & 0xff;
				cpuFeature.nFeatureInfo = CPUInfo[3];
				cpuFeature.bMultithreading = (cpuFeature.nFeatureInfo & (1 << 28)) || false;


				if( CPUInfo[3] & (1<<23) ) {
					cpuid_flags = cpuid_flags|CPUID_MMX;
				}

				if( CPUInfo[3] & (1<<25) ) {
					cpuid_flags = cpuid_flags|CPUID_SSE;
				}

				if( CPUInfo[3] & (1<<26) ) {
					cpuid_flags = cpuid_flags|CPUID_SSE2;
				}

				if( CPUInfo[2] & 0x1 ) {
					cpuid_flags = cpuid_flags|CPUID_SSE3;
				}

				if( CPUInfo[2] & (1<<9) ) {
					cpuid_flags = cpuid_flags|CPUID_SSE3_SUPPLEMENTAL;
				}

				if( CPUInfo[2] & 0x80000 ) {
					cpuid_flags = cpuid_flags|CPUID_SSE4_1;
					cpuid_flags = cpuid_flags|CPUID_SSE4;
				}

				if( CPUInfo[2] & 0x100000 ) {
					cpuid_flags = cpuid_flags|CPUID_SSE4_2;
					cpuid_flags = cpuid_flags|CPUID_SSE4;
				}

				if( cpuFeature.bMultithreading ) {
					cpuid_flags = cpuid_flags|CPUID_HTT;
				}

				if( CPUInfo[3] & (1<<15) ) {
					cpuid_flags = cpuid_flags | CPUID_CMOV;
				}
			}else if( i==4 ) {
				if( cpuid_flags & CPUID_INTEL )
					cpuFeature.nCores = ((CPUInfo[0] >> 26) & 0x3f) + 1;
			}


		}

		// Calling __cpuid with 0x80000000 as the InfoType argument
		// gets the number of valid extended IDs.
		__cpuid(CPUInfo, 0x80000000);
		nExIds = CPUInfo[0];
		memset(cpuFeature.CPUBrandString, 0, sizeof(cpuFeature.CPUBrandString));

		// get the information associated with each extended ID.
		for (i=0x80000000; i<=nExIds; ++i)
		{
			__cpuid(CPUInfo, i);

			if  (i == 0x80000001)
			{
				if( CPUInfo[2] & 0x40 ) {
					cpuid_flags = cpuid_flags | CPUID_SSE4A;
				}

				if( CPUInfo[3] & 0x40000 ) {
					cpuid_flags = cpuid_flags | CPUID_MMX_EXTENSION;
				}

				if( CPUInfo[3] & 0x80000000 ) {
					cpuid_flags = cpuid_flags | CPUID_3DNOW;
				}

				if( CPUInfo[3] & 0x40000000 ) {
					cpuid_flags = cpuid_flags | CPUID_3DNOW_EXTENSION;
				}

				if( CPUInfo[3] & (1<<29) ) {
					cpuid_flags = cpuid_flags | CPUID_EM64T;
				}

			}

			// Interpret CPU brand string and cache information.
			if  (i == 0x80000002)
				memcpy(cpuFeature.CPUBrandString, CPUInfo, sizeof(CPUInfo));
			else if  (i == 0x80000003)
				memcpy(cpuFeature.CPUBrandString + 16, CPUInfo, sizeof(CPUInfo));
			else if  (i == 0x80000004)
				memcpy(cpuFeature.CPUBrandString + 32, CPUInfo, sizeof(CPUInfo));
			else if  (i == 0x80000006)
			{
				cpuFeature.nCacheLineSize = CPUInfo[2] & 0xff;
				cpuFeature.nL2Associativity = (CPUInfo[2] >> 12) & 0xf;
				cpuFeature.nCacheSizeK = (CPUInfo[2] >> 16) & 0xffff;
			}
			else if  (i == 0x80000008)
			{
				cpuFeature.nPhysicalAddress = CPUInfo[0] & 0xff;
				cpuFeature.nVirtualAddress = (CPUInfo[0] >> 8) & 0xff;

				if( cpuid_flags & CPUID_AMD )
					cpuFeature.nCores = ((unsigned int)(CPUInfo[2] & 0xff)) + 1;

			}
		}

	}

	unsigned int SysCpu::get_processor_id()
	{
		return cpuid_flags;
	}

	void SysCpu::get_cpuInfo( cpuInfo_t& info )
	{
		info.logicalNum = cpuFeature.nlogicalProcessors;
		info.physicalNum = cpuFeature.nCores;
		info.withHyperThreads = (info.physicalNum < info.logicalNum);
	}

	const char *	SysCpu::get_processor_string() {
		return cpuFeature.CPUBrandString;
	}

	double SysCpu::get_clock_ticks_per_second()
	{
		return clock_ticks_per_second();
	}


}   // ::inf